/*
* Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: Camerality OpenGL Profiler.
*
*/

// INCLUDE FILES
#include <coemain.h>
#include <EGL/egl.h>
#include <GLES/gl.h>
#include <stdlib.h>
#include <string.h>

#include "cameralitytraces.h"
#include "cameralityglprofiler.h"

// MACROS

// CONSTANTS
typedef unsigned long long int EGLNativeThreadIdTypeNOK;

#define EGL_PROF_QUERY_GLOBAL_BIT_NOK           0x0001
#define EGL_PROF_QUERY_MEMORY_USAGE_BIT_NOK     0x0002

#define EGL_PROF_TOTAL_MEMORY_NOK               0x3070
#define EGL_PROF_USED_MEMORY_NOK                0x3071

namespace 
{
    const char * const EGL_NOK_resource_profiling = "EGL_NOK_resource_profiling";
    const char * const eglQueryProfilingDataNOK = "eglQueryProfilingDataNOK";
}


// ================= MEMBER FUNCTIONS =======================

/*!
* Two-phased constructor.
* @return Profiler instance.
*/
CCameralityGLProfiler* CCameralityGLProfiler::NewL()
    {
    CCameralityGLProfiler* self = new (ELeave) CCameralityGLProfiler();
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(self);
    return self;
    }

/*!
* Constructor.
*/
CCameralityGLProfiler::CCameralityGLProfiler() 
    : iTotalMem(0),
      iUsedMem(0)
    {
    }


/*!
* Second phase constructor.
*/
void CCameralityGLProfiler::ConstructL()
    {
    // Create an active object for animating the scene
    iPeriodic = CPeriodic::NewL(CActive::EPriorityIdle);
    }

/*!
* Destructor.
* Releases any used resources.
*/
CCameralityGLProfiler::~CCameralityGLProfiler()
    {    
    TRACE_ENTER();
    delete iPeriodic;
    eglTerminate(iEglDisplay);
    TRACE_EXIT();
    }

/*!
* Start profiling.
*/

void CCameralityGLProfiler::Start(EGLDisplay aDisplay)
    {
    TRACE_ENTER();
    if (!iEglDisplay) 
        {
        Initialize(aDisplay);
        }
    // If we have the profiling extension, profiling can be started.
    if (iEglQueryProfilingData)
        {
        if (iPeriodic && !iPeriodic->IsActive())
            {
            iPeriodic->Start(500*1000, 500*1000, 
                             TCallBack(ProfilingCallback,this));
            }
        }
    TRACE_EXIT();
    }

/*!
* Stop profiling.
*/
void CCameralityGLProfiler::Stop()
    {
    TRACE_ENTER();
    if (iPeriodic)
        {
        iPeriodic->Cancel();
        }
    if (iEglDisplay)
        {
        TRACE("Terminate display..")
        eglTerminate(iEglDisplay);
        iEglDisplay = 0;
        }
    iEGLMajor = 0;
    iEGLMinor = 0;
    iDataCount = 0;
    if (iProfilingData)
        {
        free(iProfilingData);
        iProfilingData = 0;
        }
    iEglQueryProfilingData = 0;    
    iVendor = 0;
    iVersion = 0;
    iClientAPIS = 0;
    iExtensions = 0;
    TRACE_EXIT();
    }

/*!
* 
*/
TInt CCameralityGLProfiler::FreeMemory() const
    {
    return (iTotalMem - iUsedMem);
    }

/*!
* Called by the CPeriodic for doing periodic profiling.
*/
TInt CCameralityGLProfiler::ProfilingCallback(TAny* aInstance)
    {
    // Cast parameter to container instance
    CCameralityGLProfiler* instance = (CCameralityGLProfiler*) aInstance;
    return instance->Profile();
    }

/*!
* Profile the current memory consumption etc.
*/
TInt CCameralityGLProfiler::Profile()
    {
    TRACE_ENTER();
    
    if (!iEglQueryProfilingData)
        {
        return 0;
        }
    iEglQueryProfilingData(iEglDisplay, 
                          EGL_PROF_QUERY_GLOBAL_BIT_NOK | 
                          EGL_PROF_QUERY_MEMORY_USAGE_BIT_NOK,
                          NULL,
                          0,
                          (EGLint*)&iDataCount);

    // Allocate room for the profiling data
    if (iProfilingData)
        {
        free(iProfilingData);
        iProfilingData = 0;
        }
    iProfilingData = (EGLint*)malloc(iDataCount * sizeof(EGLint));
    memset(iProfilingData, 0, iDataCount * sizeof(EGLint));

    // Retrieve the profiling data
    iEglQueryProfilingData(iEglDisplay, 
                             EGL_PROF_QUERY_GLOBAL_BIT_NOK | 
                             EGL_PROF_QUERY_MEMORY_USAGE_BIT_NOK,
                             iProfilingData,
                             iDataCount,
                             (EGLint*)&iDataCount);

    
    // Store the profiling data
    EGLint i = 0;
    while (iProfilingData && i < iDataCount)
        {
        switch (iProfilingData[i++])
            {
            case EGL_PROF_TOTAL_MEMORY_NOK:
                {
                iTotalMem = iProfilingData[i++];
                break;
                }
            case EGL_PROF_USED_MEMORY_NOK:
                {
                iUsedMem = iProfilingData[i++];
                break;
                }
            default:
                i++;
            }
        }
    
    TRACE_EXIT();
    return 0;
    }

/*!
 * Initialize the EGL and check if profiling extension is available.
 */
void CCameralityGLProfiler::Initialize(EGLDisplay aDisplay)
    {
    TRACE_ENTER();
    if (aDisplay) 
        {
        iEglDisplay = aDisplay;
        }
    else
        {
        iEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
        eglInitialize(iEglDisplay, &iEGLMajor, &iEGLMinor);
        }

    iVendor = eglQueryString(iEglDisplay, EGL_VENDOR);
    iVersion = eglQueryString(iEglDisplay, EGL_VERSION);
    iClientAPIS = eglQueryString(iEglDisplay, EGL_CLIENT_APIS);
    iExtensions = eglQueryString(iEglDisplay, EGL_EXTENSIONS);
    
    if (strstr(iExtensions, EGL_NOK_resource_profiling)) 
        {
        iEglQueryProfilingData = 
            (NOK_resource_profiling)eglGetProcAddress(eglQueryProfilingDataNOK);
        }

    // Release display if profiling extension is not found.
    if (!iEglQueryProfilingData)
        {
        eglTerminate(iEglDisplay);
        iEglDisplay = 0;
        
        iVendor = 0;
        iVersion = 0;
        iClientAPIS = 0;
        iExtensions = 0;
        }
    TRACE_EXIT();
    }

// End of file
